import time
import logging
from typing import Tuple, Optional

import requests
from django.conf import settings
from django.core.cache import cache

logger = logging.getLogger(__name__)


TOKEN_CACHE_KEY = "baidu_ai_access_token"
TOKEN_EXPIRE_SEC = 60 * 60  # 1小时，实际返回一般是30天，谨慎缓存


def _get_keys() -> Tuple[str, str]:
    # 优先使用人脸识别密钥，其次退化到 OCR 密钥（兼容旧配置）
    face_cfg = getattr(settings, "BAIDU_FACE", None)
    cfg = face_cfg if face_cfg is not None else getattr(settings, "BAIDU_OCR", {})
    api_key = cfg.get("API_KEY", "")
    secret_key = cfg.get("SECRET_KEY", "")
    return api_key, secret_key


def get_access_token(force_refresh: bool = False) -> Optional[str]:
    """
    获取百度AI Access Token，缓存至Redis/Django Cache。
    """
    if not force_refresh:
        token = cache.get(TOKEN_CACHE_KEY)
        if token:
            return token

    api_key, secret_key = _get_keys()
    if not api_key or not secret_key:
        logger.error("Baidu AI keys missing in settings.BAIDU_OCR")
        return None

    url = (
        "https://aip.baidubce.com/oauth/2.0/token"
        "?grant_type=client_credentials"
        f"&client_id={api_key}"
        f"&client_secret={secret_key}"
    )
    try:
        resp = requests.post(url, timeout=8)
        data = resp.json()
        token = data.get("access_token")
        if token:
            cache.set(TOKEN_CACHE_KEY, token, TOKEN_EXPIRE_SEC)
            return token
        logger.error("Failed to get access_token: %s", data)
        return None
    except Exception as e:
        logger.exception("Get access token error: %s", str(e))
        return None


def verify_liveness_face(image_base64: str) -> Tuple[bool, str, float]:
    """
    使用百度人脸校验接口进行单张活体检测。
    采用 v3 faceverify 接口，判定阈值为 liveness > 0.8 通过。
    """
    token = get_access_token()
    if not token:
        return False, "获取百度鉴权失败", 0.0

    url = (
        "https://aip.baidubce.com/rest/2.0/face/v3/faceverify"
        f"?access_token={token}"
    )
    headers = {"Content-Type": "application/json"}
    # 按官方文档，数组每项包含 image/image_type/face_field/liveness_control
    payload = [
        {
            "image": image_base64,
            "image_type": "BASE64",
            "face_field": "quality",
            "liveness_control": "HIGH",
        }
    ]

    try:
        resp = requests.post(url, json=payload, headers=headers, timeout=10)
        data = resp.json()
        if data.get("error_code") != 0:
            # 令牌过期重试一次
            if data.get("error_code") in (110, 111):
                token = get_access_token(force_refresh=True)
                if not token:
                    return False, "鉴权失败", 0.0
                url = (
                    "https://aip.baidubce.com/rest/2.0/face/v3/faceverify"
                    f"?access_token={token}"
                )
                resp = requests.post(
                    url, json=payload, headers=headers, timeout=10
                )
                data = resp.json()

        if data.get("error_code") != 0:
            code = data.get("error_code")
            msg = data.get("error_msg", "活体检测失败")
            # 友好提示：无权限通常是使用了错误产品的AK/SK或未开通人脸识别
            if code == 6:
                msg = "百度人脸接口无权限，请使用人脸识别产品的AK/SK并确认已开通"
                # 开发兜底：允许在设置了 LIVENESS_DEV_ALLOW=True 时临时放行
                if getattr(settings, "LIVENESS_DEV_ALLOW", False):
                    logger.warning("DEV ALLOW: bypass liveness due to permission error (code=6)")
                    return True, "dev-allow", 0.0
            logger.warning("Baidu liveness api failed: %s", data)
            return False, msg, 0.0

        result = data.get("result") or {}
        # v3 接口通常返回 result['face_liveness']，有的版本为
        # face_list[0]['liveness']，这里兼容两种
        score = result.get("face_liveness")
        if score is None:
            face_list = result.get("face_list") or []
            if face_list and isinstance(face_list, list):
                score = face_list[0].get("liveness")

        try:
            score_val = float(score) if score is not None else 0.0
        except Exception:
            score_val = 0.0

        # 调试输出活体分数
        logger.info("Liveness score: %s", score_val)
        try:
            print(f"[活体认证分数] score={score_val}")
        except Exception:
            print(f"[活体认证分数] score={score_val}")

        # 简单阈值判定
        if score_val >= 0.8:
            logger.info("Liveness result: PASS (>= 0.8), score=%.4f", score_val)
            return True, "ok", score_val
        logger.info("Liveness result: FAIL (< 0.8), score=%.4f", score_val)
        error_msg = f"活体分数过低，请重新拍摄，确保光线充足、人脸清晰"
        return False, error_msg, score_val
    except Exception as e:
        logger.exception("Call baidu faceverify error: %s", str(e))
        return False, "第三方服务异常，请稍后重试", 0.0

import requests
import json
import logging
from django.conf import settings

logger = logging.getLogger(__name__)

class BaiduLivingClient:
    """百度度百度智能云活体检测客户端    用于验证用户是否为真人，防止照片/视频伪造
    """
    def __init__(self):
        self.api_key = settings.BAIDU_LIVING_API_KEY
        self.secret_key = settings.BAIDU_LIVING_SECRET_KEY
        self.access_token = self._get_access_token()

    def _get_access_token(self):
        """获取百度接口接口调用令牌
        """
        url = f"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={self.api_key}&client_secret={self.secret_key}"
        try:
            response = requests.get(url, timeout=5)
            result = response.json()
            if "access_token" in result:
                return result["access_token"]
            logger.error(f"获取百度活体token失败: {result}")
            return None
        except Exception as e:
            logger.error(f"获取百度活体token异常: {str(e)}")
            return None

    def detect_liveness(self, image_base64):
        """调用动态活体检测接口
        Args:
            image_base64: 人脸图片的base64编码（不含前缀）
        Returns:
            dict: 检测结果，包含是否通过及分数
        """
        if not self.access_token:
            return {"success": False, "error": "未获取到访问令牌"}

        url = f"https://aip.baidubce.com/rest/2.0/face/v3/faceverify/liveness?access_token={self.access_token}"
        headers = {"Content-Type": "application/json"}
        data = {
            "image": image_base64,
            "image_type": "BASE64",
            "liveness_control": "NORMAL"  # 中等严格度
        }

        try:
            response = requests.post(url, headers=headers, data=json.dumps(data), timeout=10)
            result = response.json()
            logger.info(f"百度活体检测原始结果: {result}")

            # 解析结果
            if result.get("error_code") == 0:
                face_liveness = result["result"]["face_liveness"]
                thresholds = result["result"]["thresholds"]["frr_1e-4"]  # 万分之一误识率阈值
                return {
                    "success": True,
                    "is_living": face_liveness >= thresholds,  # 真人判断
                    "score": face_liveness,
                    "threshold": thresholds
                }
            else:
                error_msg = f"检测失败: {result.get('error_msg', '未知错误')}"
                logger.error(error_msg)
                return {"success": False, "error": error_msg}
        except Exception as e:
            logger.error(f"活体检测接口调用异常: {str(e)}")
            return {"success": False, "error": f"接口调用失败: {str(e)}"}